/**
 * 
 */
package it.battlehorse.scripting.registry.impl;

import it.battlehorse.rcp.sl.annot.Injected;
import it.battlehorse.rcp.sl.annot.Serviceable;
import it.battlehorse.scripting.IScriptingConstants;
import it.battlehorse.scripting.registry.IScript;
import it.battlehorse.scripting.registry.IScriptingRegistry;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;


/**
 * Provides the default implementation for the scripting registry
 * 
 * @author battlehorse
 *
 */
@Serviceable
public class BasicScriptingRegistry implements IScriptingRegistry, IScriptingConstants {
	
	private Log log ; 
	
	@Injected
	public void setLog(Log log) {
		this.log = log;
	} 

	/* collects all the available scripts, the key is the script id */
	private Map<String,IScript> scripts;
	
	/**
	 * The default implementation of the <code>IScript</code> interface
	 * 
	 * @author battlehorse
	 *
	 */
	private class BasicScript implements IScript {
		
		private String extension;
		private String uri;
		private String id;
		private String namespace;
		private boolean modal;
		
		public BasicScript(String id, String uri,String extension, String namespace, boolean modal) {
			this.id = id;
			this.uri = uri;
			this.extension = extension;
			this.namespace = namespace;
			this.modal = modal;
		}
		
		/**
		 * @return the extension
		 */
		public String getExtension() {
			return extension;
		}

		/**
		 * @return the uri
		 */
		public String getURI() {
			return uri;
		}
		
		/**
		 * @return the id
		 */
		public String getId() {
			return id;
		}
		
		/**
		 * @return the namespace
		 */
		public String getNamespace() {
			return namespace;
		}
		
		/**
		 * @return the modality
		 */
		public boolean isModal() {
			return modal;
		}
		
		/**
		 * Returns a reader which points to the script source code. This method works both for 
		 * external scripts and internal (i.e. saved into the plugin jar files) scripts.
		 * 
		 * @return a reader which points to the script source code, or {@code null} if it is impossible to locate
		 * 		the script source code
		 */
		public Reader getReader() throws IOException {
			try {
				URI uri = new URI(getURI());
				if (uri.isAbsolute() && "file".equals(uri.getScheme())) {
					Reader r = new BufferedReader(new FileReader(new File(new URI(getURI()))));
					return r;
				}
				else {
					// assume the URI is local resource reference. Ask to bundle it 
					// belongs to to load it
					String ns = getNamespace();
					if (ns != null) {
						Bundle b = Platform.getBundle(ns);
						if (b != null) {
							Reader r = new BufferedReader(new InputStreamReader(b.getResource(getURI()).openStream()));
							return r;							
						}
						else
							return null;
					}
					else
						return null;
					
					
					
				}
			}
			catch(URISyntaxException use) {
				throw new IOException(use);				
			}
		}
		
		@Override
		public String toString() {
			return "BasicScript(id=" + id + ",uri=" + uri + ",extension=" + extension + ")";
		}
	}

	public BasicScriptingRegistry() {
		scripts = new ConcurrentHashMap<String,IScript>();
	}	
	
	/**
	 * Adds the given script to the registry
	 * 
	 * @param id the id of the eclipse wrapper contribution which wraps this script
	 * @param scriptId the script id
	 * @param uri the script URI
	 * @param extension the script extension
	 * @param namespace the script namespace
	 */
	public void put(String id, String scriptId, String uri, String extension, String namespace) {
		scripts.put(id, new BasicScript(scriptId, uri,extension, namespace,false));
	}
	
	/**
	 * Adds the given script to the registry
	 * 
	 * @param id the id of the eclipse wrapper contribution which wraps this script
	 * @param scriptId the script id
	 * @param uri the script URI
	 * @param extension the script extension
	 * @param namespace the script namespace
	 * @param modal the script modality
	 */
	public void put(String id, String scriptId, String uri, String mimeType, String namespace, boolean modal) {
		scripts.put(id, new BasicScript(scriptId, uri,mimeType, namespace, modal));
	}
	
	
	/**
	 * Adds the given script to the registry
	 * 
	 * @param id the id of the eclipse wrapper contribution which wraps this script
	 * @param script the script
	 */
	public void put(String id, IScript script) {
		scripts.put(id,script);		
	}
	
	/*
	 * (non-Javadoc)
	 * @see it.battlehorse.scripting.registry.IScriptingRegistry#getScriptFromId(java.lang.String)
	 */
	public IScript getScriptFromId(String id) {
		return scripts.get(id);
	}
	
	/*
	 * (non-Javadoc)
	 * @see it.battlehorse.scripting.registry.IScriptingRegistry#getScriptObject(java.lang.String)
	 */
	public Object getScriptObject(String id) {
		
		IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(ID_SCRIPTED_CONTRIBUTION);
		for (IExtension ext: point.getExtensions()) {			
			for (IConfigurationElement el : ext.getConfigurationElements()) {
				if (el.getName().equals(EL_SCRIPT_OBJECT) && el.getAttribute("id").equals(id)) {
					try {
						return el.createExecutableExtension("class");
					}
					catch(CoreException e) {
						log.error("Error while creating script object " + el.getAttribute("id"),e);
					}
				}
			}
		}
		
		return null;
	}

}
